﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.ComponentModel;
using System.Windows;
using System.Xml.Serialization;

namespace AssignmentsManage.DataClasses
{
    [XmlRootAttribute(ElementName = "DataHolder", IsNullable = false)]
    public class DataHolder : DependencyObject, INotifyPropertyChanged
    {
        #region Constructors

        /// <summary>
        /// Default constructor
        /// </summary>
        public DataHolder()
        {
            //Set lists to 'new'
            CourseList = new ObservableCollection<Course>();
            ActivityList = new ObservableCollection<Activity>();

            //Setup event handers
            CourseList.CollectionChanged += new System.Collections.Specialized.NotifyCollectionChangedEventHandler(CourseList_CollectionChanged);
            ActivityList.CollectionChanged += new System.Collections.Specialized.NotifyCollectionChangedEventHandler(ActivityList_CollectionChanged);
        }

        #endregion

        #region Properties

        private DateTime _LastSave;
        /// <summary>
        /// The last date and time the file was saved
        /// </summary>
        [XmlElement("LastSave")]
        public DateTime LastSave
        {
            get
            {
                return _LastSave;
            }
            set
            {
                _LastSave = value;
                OnPropertyChanged("LastSave");
            }
        }

        private ObservableCollection<Course> _CourseList;
        /// <summary>
        /// Collection containing all courses
        /// </summary>
        [XmlArray("Courses"), XmlArrayItem("Course", typeof(Course))]
        public ObservableCollection<Course> CourseList
        {
            get
            {
                return _CourseList;
            }
            set
            {
                _CourseList = value;
            }
        }

        private ObservableCollection<Activity> _ActivityList;
        /// <summary>
        /// Collection containing all activities
        /// </summary>
        [XmlArray("Activities"), XmlArrayItem("Activity", typeof(Activity))]
        public ObservableCollection<Activity> ActivityList
        {
            get
            {
                return _ActivityList;
            }
            set
            {
                _ActivityList = value;
            }
        }

        [XmlIgnore()]
        public int TotalActivityCount
        {
            get
            {
                return ActivityList.Count;
            }
        }

        #endregion

        #region EventHandlers

        void ActivityList_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
        {
            OnPropertyChanged("TotalActivityCount");
        }

        void CourseList_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
        {
            //Delete activities of the course being removed
            if (e.Action == System.Collections.Specialized.NotifyCollectionChangedAction.Remove)
            {
                foreach (object item in e.OldItems)
                {
                    List<int> deleteList = new List<int>();
                    Course courseItem = (Course)item;
                    for (int i = ActivityList.Count - 1; i >= 0; i--)
                    {
                        if (courseItem.CourseCode == ActivityList[i].CourseInCode)
                        {
                            deleteList.Add(i);
                        }
                    }

                    foreach (int d in deleteList)
                    {
                        ActivityList.RemoveAt(d);
                    }
                }
            }
        }

        #endregion

        #region General Methods

        /// <summary>
        /// Add a new activity to the collection in sorted order.
        /// </summary>
        /// <param name="newActivity">New activity object to add</param>
        public void InsertActivityByDueDate(Activity newActivity)
        {
            int i = 0;
            DateTime dtOn = ActivityList[i].DueDate.Value;

            while (dtOn < newActivity.DueDate)
            {
                i++;
                dtOn = ActivityList[i].DueDate.Value;
            }

            ActivityList.Insert(i, newActivity);
        }

        /// <summary>
        /// Get a course by its course code.
        /// </summary>
        /// <param name="courseCode">CourseCode of the course to return</param>
        /// <returns>If found returns the course with the correct course code. Otherwise returns null.</returns>
        public Course getCourseByCode(string courseCode)
        {
            List<Course> listGen = CourseList.Where(t=> t.CourseCode == courseCode).ToList<Course>();
            if (listGen.Count > 0)
            {
                return listGen[0];
            }
            else
            {
                return null;
            }
        }

        public void replaceCourseCodeForActivities(string oldCode, string newCode)
        {
            if (oldCode != newCode)
            {
                foreach (Activity activity in ActivityList)
                {
                    if (activity.CourseInCode == oldCode)
                    {
                        activity.CourseInCode = newCode;
                    }
                }
            }
        }

        public void deleteCourse(Course item, bool deleteTasks = true)
        {
            deleteCourse(item.CourseCode, deleteTasks);
        }

        public void deleteCourse(string courseCode, bool deleteTasks = true)
        {
            Course delCourse = getCourseByCode(courseCode);
            if (delCourse != null)
            {
                if (deleteTasks)
                {
                    ActivityList = new ObservableCollection<Activity>(ActivityList.Where(x => x.CourseInCode != courseCode).ToList<Activity>());
                }
                CourseList.Remove(delCourse);
            }
        }

        #endregion

        #region Data Methods

        public double getTotalCourseGrade(string courseCode)
        {
            return getTotalCourseGrade(getCourseByCode(courseCode));
        }

        public double getTotalCourseGrade(Course courseOf)
        {
            if (courseOf != null)
            {
                ////TODO: Test this code

                List<ActivityWorthValueStruct> totals = new List<ActivityWorthValueStruct>();

                bool foundGreater = false;

                foreach (ActivityTypeWorth worth in courseOf.ActivityTypeWorths)
                {
                    if (worth.Worth > 0)
                    {
                        foundGreater = true;

                        double tTop = 0;
                        double tBottom = 0;

                        foreach (Activity activity in ActivityList.Where(x => x.CourseInCode == courseOf.CourseCode && x.ActivityStatusType == EnumInfo.ActivityStatusType.Graded))
                        {
                            if (activity.Grade != null)
                            {
                                tTop += activity.Grade.Numerator;
                                tBottom += activity.Grade.Denominator;
                            }
                        }

                        double total = tBottom > 0 ? (tTop / tBottom) * worth.Worth : -1;
                        totals.Add(new ActivityWorthValueStruct(worth, total));
                    }
                }
                if (foundGreater)
                {

                    double fTop = 0;
                    double fBottom = 0;
                    foreach (var t in totals)
                    {
                        if (t.Value > -1)
                        {
                            fTop += t.Value;
                            fBottom += t.Worth.Worth;
                        }
                    }

                    double final = fTop / fBottom * 100;
                    return final;
                }
                else
                {
                    double tTop = 0;
                    double tBottom = 0;
                    foreach (Activity activity in ActivityList.Where(x => x.CourseInCode == courseOf.CourseCode && x.ActivityStatusType == EnumInfo.ActivityStatusType.Graded))
                    {
                        if (activity.Grade != null)
                        {
                            tTop += activity.Grade.Numerator;
                            tBottom += activity.Grade.Denominator;
                        }
                    }
                    double final = tTop / tBottom * 100;
                    return final;
                }
            }
            else
            {
                return Double.NaN;
            }
        }

        #endregion

        #region NotifyChanges

        // Create the OnPropertyChanged method to raise the event
        protected void OnPropertyChanged(string name)
        {
            PropertyChangedEventHandler handler = PropertyChanged;
            if (handler != null)
            {
                handler(this, new PropertyChangedEventArgs(name));
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;

        #endregion

        #region Structs

        public struct ActivityWorthValueStruct
        {
            public ActivityWorthValueStruct(ActivityTypeWorth worth, double value)
            {
                Worth = worth;
                Value = value;
            }

            public ActivityTypeWorth Worth;
            public double Value;
        }

        #endregion
    }
}
